{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _nb_mixed_variable:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Mixed Variable Problem\n",
    "\n",
    "In some cases, variables might have different types; some might be real, discrete (choice), binary, or integer-valued. For those cases, different evolutionary operators must be applied to different types of variables. In **pymoo** this is supported by defining a `MixedVariableProblem` where the `vars` values are set. For example, let us consider the following optimization problem:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.core.problem import ElementwiseProblem\n",
    "from pymoo.core.variable import Real, Integer, Choice, Binary\n",
    "\n",
    "\n",
    "class MixedVariableProblem(ElementwiseProblem):\n",
    "\n",
    "    def __init__(self, **kwargs):\n",
    "        vars = {\n",
    "            \"b\": Binary(),\n",
    "            \"x\": Choice(options=[\"nothing\", \"multiply\"]),\n",
    "            \"y\": Integer(bounds=(0, 2)),\n",
    "            \"z\": Real(bounds=(0, 5)),\n",
    "        }\n",
    "        super().__init__(vars=vars, n_obj=1, **kwargs)\n",
    "\n",
    "    def _evaluate(self, X, out, *args, **kwargs):\n",
    "        b, x, z, y = X[\"b\"], X[\"x\"], X[\"z\"], X[\"y\"]\n",
    "\n",
    "        f = z + y\n",
    "        if b:\n",
    "            f = 100 * f\n",
    "\n",
    "        if x == \"multiply\":\n",
    "            f = 10 * f\n",
    "\n",
    "        out[\"F\"] = f\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to solve such a problem, pymoo offers `MixedVariableGA`, which defines different operators for each variable type. For more details, please look at the implementation itself."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from pymoo.core.mixed import MixedVariableGA\n",
    "from pymoo.core.variable import Real, Integer\n",
    "from pymoo.optimize import minimize\n",
    "\n",
    "problem = MixedVariableProblem()\n",
    "\n",
    "algorithm = MixedVariableGA(pop_size=10)\n",
    "\n",
    "res = minimize(problem,\n",
    "               algorithm,\n",
    "               termination=('n_evals', 1000),\n",
    "               seed=1,\n",
    "               verbose=False)\n",
    "\n",
    "print(\"Best solution found: \\nX = %s\\nF = %s\" % (res.X, res.F))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Moreover, for single-objective optimization, the well-known Hyperparameter optimization framework [Optuna](https://optuna.org) can be used (pymoo only wraps to their interface here. Congrats on their excellent work!)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymoo.algorithms.soo.nonconvex.optuna import Optuna\n",
    "from pymoo.core.variable import Real, Integer\n",
    "from pymoo.optimize import minimize\n",
    "\n",
    "problem = MixedVariableProblem()\n",
    "\n",
    "algorithm = Optuna()\n",
    "\n",
    "res = minimize(problem,\n",
    "               algorithm,\n",
    "               termination=('n_evals', 300),\n",
    "               seed=1,\n",
    "               verbose=False)\n",
    "\n",
    "print(\"Best solution found: \\nX = %s\\nF = %s\" % (res.X, res.F))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Moreover, if you intend to solve a multi-objective optimization problem, you can either instantiate existing algorithms with `MixedVariableMating` or add a multi-objective survival to `MixedVariableGA`. The latter can be realized, for instance by:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MultiObjectiveMixedVariableProblem(ElementwiseProblem):\n",
    "\n",
    "    def __init__(self, **kwargs):\n",
    "        vars = {\n",
    "            \"b\": Binary(),\n",
    "            \"x\": Choice(options=[\"nothing\", \"multiply\"]),\n",
    "            \"y\": Integer(bounds=(-2, 2)),\n",
    "            \"z\": Real(bounds=(-5, 5)),\n",
    "        }\n",
    "        super().__init__(vars=vars, n_obj=2, n_ieq_constr=0, **kwargs)\n",
    "\n",
    "    def _evaluate(self, X, out, *args, **kwargs):\n",
    "        b, x, z, y = X[\"b\"], X[\"x\"], X[\"z\"], X[\"y\"]\n",
    "\n",
    "        f1 = z ** 2 + y ** 2\n",
    "        f2 = (z+2) ** 2 + (y-1) ** 2\n",
    "\n",
    "        if b:\n",
    "            f2 = 100 * f2\n",
    "\n",
    "        if x == \"multiply\":\n",
    "            f2 = 10 * f2\n",
    "\n",
    "        out[\"F\"] = [f1, f2]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pymoo.visualization.scatter import Scatter\n",
    "from pymoo.algorithms.moo.nsga2 import RankAndCrowdingSurvival\n",
    "from pymoo.core.mixed import MixedVariableGA\n",
    "from pymoo.optimize import minimize\n",
    "\n",
    "problem = MultiObjectiveMixedVariableProblem()\n",
    "\n",
    "algorithm = MixedVariableGA(pop_size=20, survival=RankAndCrowdingSurvival())\n",
    "\n",
    "res = minimize(problem,\n",
    "               algorithm,\n",
    "               ('n_gen', 50),\n",
    "               seed=1,\n",
    "               verbose=False)\n",
    "\n",
    "plot = Scatter()\n",
    "plot.add(problem.pareto_front(), plot_type=\"line\", color=\"black\", alpha=0.7)\n",
    "plot.add(res.F, facecolor=\"none\", edgecolor=\"red\")\n",
    "plot.show()"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
